// s.d(t, { z: () => Chunk })
import * as THREE from "three"
import configConst from "../constant/config.const"
import lodEnum from "../enum/lod.enum"
import meshEnum from "../enum/mesh.enum"
import renderType from "../enum/renderType.enum"
import { createSubscription } from "../utils/Subscription"
import funcUtil from "../utils/func.util"
import n from "./321-19131"
import { ShaderConfig } from "../constant/shader.const"
import ChunkMaterial from "../material/ChunkMaterial"

let m
let g = 1
try {
  m = n.k7(0, 0)
} catch (e) {}
const p = [
  {
    key: meshEnum.PanoTextureTransition,
    enabled: function (e) {
      return e.progress.value > 0 && e.progress.value < 1 && e.pano0Map.value !== e.pano1Map.value
    },
    dependsOn: [meshEnum.PanoTexture],
    uniformsUsed: ["progress", "pano0Map", "pano1Map"]
  },
  {
    key: meshEnum.PanoTexture,
    enabled: function (e) {
      return e.panoOpacity.value > 0
    },
    dependsOn: [],
    uniformsUsed: ["panoOpacity"]
  },
  {
    key: meshEnum.ColorOverlay,
    enabled: function (e) {
      return null !== e.colorOverlay.value
    },
    dependsOn: [],
    uniformsUsed: ["colorOverlay"]
  },
  {
    key: meshEnum.MeshPreviewSphere,
    enabled: function (e) {
      return null !== e.meshPreviewCenter.value
    },
    dependsOn: [meshEnum.MeshTexture],
    uniformsUsed: []
  },
  {
    key: meshEnum.MeshTexture,
    enabled: function (e) {
      return e.meshOpacity.value > 0 && e.map.value
    },
    dependsOn: [],
    uniformsUsed: ["meshOpacity", "map"]
  },
  {
    key: meshEnum.Wireframe,
    enabled: function (e) {
      return !1
    },
    dependsOn: [],
    uniformsUsed: []
  },
  {
    key: meshEnum.FlatShading,
    enabled: function (e) {
      return !1
    },
    dependsOn: [],
    uniformsUsed: []
  },
  {
    key: meshEnum.PanoOverlay,
    enabled: function (e) {
      return !!e.overlay0Map.value
    },
    dependsOn: [meshEnum.PanoTexture],
    uniformsUsed: ["overlay0Map"]
  },
  {
    key: meshEnum.PanoOverlayTransition,
    enabled: function (e) {
      return !!(e.progress.value > 0 && e.progress.value < 1 && e.overlay0Map.value && e.overlay1Map.value && e.overlay0Map.value !== e.overlay1Map.value)
    },
    dependsOn: [meshEnum.PanoOverlay, meshEnum.PanoTexture, meshEnum.PanoTextureTransition],
    uniformsUsed: ["progress", "overlay0Map", "overlay1Map"]
  },
  {
    key: meshEnum.MeshTrim,
    enabled: function (e) {
      return e.meshTrimsEnabled.value.some(e => e)
    },
    dependsOn: [meshEnum.MeshTexture],
    uniformsUsed: ["meshTrimPositions"]
  }
]
const f = new Set(p.map(e => e.uniformsUsed).reduce((e, t) => e.concat(t), []))
export default class Chunk {
  meshGroup: any
  meshSubgroup: any
  geometry: any
  textureName: string
  id: number
  name: string
  lod: number
  renderingMode: number
  chunkMaterials: any
  modeMaterials: any
  capabilityOverrides: any
  onMaterialUpdate: Set<any>
  uniformCache: any
  temp: { m1: THREE.Matrix4; m2: THREE.Matrix4; quat: THREE.Quaternion }
  _material: THREE.MeshBasicMaterial
  _colorOverlay: any
  _meshTextureOpacity: any
  _progress: any
  onOpacityUpdate: any
  static z: any
  constructor(e, t, s, a = "") {
    ;(this.meshGroup = e),
      (this.meshSubgroup = t),
      (this.geometry = s),
      (this.textureName = a),
      (this.id = g++),
      (this.name = ""),
      (this.lod = lodEnum.Standard),
      (this.renderingMode = renderType.Standard),
      (this.chunkMaterials = {}),
      (this.modeMaterials = {}),
      (this.capabilityOverrides = {}),
      (this.onMaterialUpdate = new Set()),
      (this.uniformCache = this.getUniformDefaults()),
      (this.temp = {
        m1: new THREE.Matrix4(),
        m2: new THREE.Matrix4(),
        quat: new THREE.Quaternion()
      }),
      (this.modeMaterials[renderType.Standard] = this.getChunkMaterial(this.getCapabilities())),
      (this._material = this.modeMaterials[renderType.Standard])
    const o = THREE.UniformsUtils.clone(ShaderConfig.depth.uniforms)
    this.modeMaterials[renderType.Depth] = new THREE.RawShaderMaterial({
      fragmentShader: ShaderConfig.depth.fragmentShader,
      vertexShader: ShaderConfig.depth.vertexShader,
      uniforms: o,
      side: THREE.FrontSide,
      name: "materialDepth"
    })
    const n = THREE.UniformsUtils.clone(ShaderConfig.modelOutside.uniforms)
    ;(n.opacity.value = 0.2),
      (this.modeMaterials[renderType.Transparent] = new THREE.RawShaderMaterial({
        fragmentShader: ShaderConfig.modelOutside.fragmentShader,
        vertexShader: ShaderConfig.modelOutside.vertexShader,
        uniforms: n,
        side: THREE.FrontSide,
        transparent: !0,
        name: "materialTransparent"
      }))
    const h = THREE.UniformsUtils.clone(ShaderConfig.modelOutside.uniforms)
    ;(h.opacity.value = 0.5),
      h.colorOverlay.value.set(1, 1, 1, 1),
      (this.modeMaterials[renderType.Wireframe] = new THREE.RawShaderMaterial({
        fragmentShader: ShaderConfig.modelOutside.fragmentShader,
        vertexShader: ShaderConfig.modelOutside.vertexShader,
        uniforms: h,
        side: THREE.FrontSide,
        transparent: !0,
        wireframe: !0,
        name: "materialWireframe"
      }))
  }
  getUniformDefaults() {
    const e = {}
    for (const t in ShaderConfig.modelChunk.uniforms) {
      const s = THREE.UniformsUtils.clone(ShaderConfig.modelChunk.uniforms[t])
      for (const t in s) e[t] = s[t]
    }
    return e
  }
  set material(e) {
    if (this._material !== e && this.onMaterialUpdate) for (const t of this.onMaterialUpdate.values()) t(e)
    this._material = e
  }
  get material() {
    return this._material
  }
  notifyOnMaterialUpdated(e) {
    return createSubscription(
      () => this.onMaterialUpdate.add(e),
      () => this.onMaterialUpdate.delete(e),
      !0
    )
  }
  setMeshTexture(e) {
    this.setMaterialsUniform({ map: e })
  }
  getColorOverlay() {
    return this._colorOverlay
  }
  setColorOverlay(e) {
    this._colorOverlay !== e && (this.setMaterialsUniform({ colorOverlay: e }), (this._colorOverlay = e))
  }
  setMeshTextureOpacity(e) {
    e !== this._meshTextureOpacity && (this.setMaterialsUniform({ meshOpacity: e, panoOpacity: 1 - e }), (this._meshTextureOpacity = e))
  }
  setProgress(e) {
    this._progress !== e && (this.setMaterialsUniform({ progress: e }), (this._progress = e))
  }
  setOpacity(e) {
    ;(this.material.opacity = e),
      (this.material.transparent = e < configConst.fadeConfig.FADE_OPAQUE),
      (this.material.depthWrite = e >= configConst.fadeConfig.FADE_DEPTH_WRITE_THRESHOLD),
      this.onOpacityUpdate && this.onOpacityUpdate(e)
  }
  setTime(e) {
    this.renderingMode === renderType.Wireframe && this.setMaterialsUniform({ time: e })
  }
  setTransparentDiscard(e) {
    this.setMaterialsUniform({ transparentDiscardDist: e })
  }
  setMeshPreviewSphere(e, t = 0.3) {
    this.setMaterialsUniform({
      meshPreviewCenter: e,
      meshPreviewSize: t
    })
  }
  setWireframe(e) {
    if (e) {
      if (this.geometry.getIndex()) {
        const e = this.geometry
        e.boundsTree && (e.boundsTree.geometry = this.geometry.clone()), this.geometry.copy(this.geometry.toNonIndexed())
      }
      n.ko(this.geometry)
    }
    this.overrideCapability(meshEnum.Wireframe, e)
  }
  setFlatShading(e) {
    e && this.geometry.computeVertexNormals(), this.overrideCapability(meshEnum.FlatShading, e)
  }
  getCapabilities() {
    const e = new Set()
    for (const t in this.capabilityOverrides) this.capabilityOverrides[t] && e.add(t)
    for (const t of p)
      if (!e.has(t.key) && t.enabled(this.uniformCache)) {
        e.add(t.key)
        for (const s of t.dependsOn) e.add(s)
      }
    return e
  }
  overrideCapability(e, t) {
    ;(this.capabilityOverrides[e] = t), this.updateMaterialCapabilities()
  }
  updateMaterialCapabilities() {
    const e = this.getCapabilities(),
      t = this.modeMaterials[renderType.Standard]
    if (funcUtil.sameMap(t.capabilities, e)) return this._material
    const s = this.getChunkMaterial(e)
    return (
      (s.side = t.side),
      (s.transparent = t.transparent),
      (s.depthWrite = t.depthWrite),
      (this.modeMaterials[renderType.Standard] = s),
      this.renderingMode === renderType.Standard && (this.material = s),
      this._material
    )
  }
  getChunkMaterial(e) {
    let t = "chunkMaterial_"
    for (const s of p) t += e.has(s.key) ? "1" : "0"
    return this.chunkMaterials[t] || (this.chunkMaterials[t] = new ChunkMaterial(t, e, this.getUniformsForCapabilities(e))), this.chunkMaterials[t]
  }
  getUniformsForCapabilities(e) {
    const t = {}
    for (const s of e) {
      const e = ShaderConfig.modelChunk.uniforms[s]
      for (const s in e) t[s] = this.uniformCache[s]
    }
    return t
  }
  getCurrentUniformValues(e) {
    for (const t of Object.keys(this.uniformCache)) e[t] = this.uniformCache[t].value
  }
  setMaterialsUniform(e) {
    let t,
      s = !1
    const r = this.uniformCache.meshPreviewCenter.value
    for (const i in e) {
      let a = !1
      const o = e[i]
      if (((a = "meshPreviewCenter" !== i ? a || f.has(i) : a || (null === r) != (null === o)), !(i in this.uniformCache)))
        throw new Error(`Uniform ${i} does not exist in Chunk`)
      const n = this.uniformCache[i]
      n.value !== o &&
        (o && (o.isVector3 || o.isVector4 || o.isMatrix3 || o.isMatrix4 || o.isColor)
          ? null === n.value
            ? (n.value = o.clone())
            : n.value.copy(o)
          : (n.value = o),
        (s = s || a),
        "opacity" === i && (t = o))
    }
    return s && this.updateMaterialCapabilities(), void 0 !== t && this.setOpacity(t), this._material
  }
  setRenderingMode(e) {
    ;(this.renderingMode = e), (this.material = this.modeMaterials[e])
  }
  setSide(e) {
    for (const t in this.chunkMaterials) this.chunkMaterials[t].side = e
  }
  forEachMaterial(e) {
    for (const t in this.chunkMaterials) e(this.chunkMaterials[t])
  }
  setProjectedPano(e, t, s, r) {
    let i = 1 === e ? "pano1Map" : "pano0Map"
    const a = {}
    ;(a[i] = r || m),
      t && ((i = 1 === e ? "pano1Position" : "pano0Position"), (a[i] = t)),
      s &&
        t &&
        ((i = 1 === e ? "pano1Matrix" : "pano0Matrix"),
        this.temp.m1.makeRotationFromQuaternion(this.temp.quat.copy(s).invert()),
        this.temp.m2.makeScale(-1, 1, 1),
        (a[`${i}1`] = this.temp.m1),
        (a[`${i}2`] = this.temp.m2)),
      this.setMaterialsUniform(a)
  }
  setOverlayPano(e, t, s) {
    const i = `overlay${e}`,
      a = {}
    if (t) {
      const e = new THREE.Matrix4().makeRotationFromQuaternion(t)
      a[i + "Matrix"] = e
    }
    ;(a[i + "Map"] = s || m), this.setMaterialsUniform(a)
  }
}
